package main

import (
	"fmt"
	"reflect"
)

type Student00 struct {
	Name string
	Age  int `a:"1111" b:"3333"`
	/*
	   结构体标签提供了关于结构体字段的元数据，这些元数据可以在运行时使用反射机制获取。
	   结构体标签由一对反引号括起来，里面是一个或多个以空格分隔的键值对。
	   每个键值对中的键表示标签的名称，值则是该标签的具体内容，不同的键值对之间也可以用空格或逗号进行分隔。
	       在本例中，`Age` 字段的结构体标签定义为 `a:"1111" b:"3333"`，
	       其中 `a` 和 `b` 是标签的名称，而 `"1111"` 和 `"3333"` 是它们各自的值。
	   这些标签的目的和含义是完全由代码使用者自己定义的，例如可以用于指定 JSON 序列化时的字段名，或者用于数据库查询时的条件等。
	*/
}

func main() {
	/*
		所有类型通用的方法：
			1.返回包含包名的类型名字，对未命名类型返回的是空
			Name() string

			2.kind 返回该类型的底层基础类型
			Kind() Kind

			3.确定当前类型是否实现了 u 接口类型
				注意这里的 u 必须是接口类型的 Type
			Implements(u Type) bool

			4.判断当前类型的实例是否能够赋值给 type 为 u 的类型变量
			AssignableTo(u Type) bool

			5.判断当前类型的实例是否能强制类型转换为 u 类型变量
			ConvertibleTo(u Type) bool

			6.判断当前类型是否支持比较（等于或不等于）
			Comparable() bool

			7.返回一个类型的方法个数
			NumMethod() int

			8.通过索引值访问方法，索引值必须属于[0, NumMethod()]，否则引发 panic
			Method(int) Method

			9.通过方法名获取Method
			MethodByName(string) (Method, bool)

		 	10.返回类型的包路径，如果类型是预声明类型或未命名类型，则返回空字符串
			PckPath() string

			11.返回存放该类型的实例需要多大的字节空间
			Size() uintptr

		不同基础类型的专有方法：
			1.返回 类型 的 元素类型，该方法只适合 array, chan, map, ptr, slice 类型
			Elem() Type

			2.返回 数值型类型 内存占用的位数
			Bits() int

			3.struct 类型专用的方法
			（1）返回字段数目
				NumField() int
			（2）通过整数索引获取 struct 字段
				Field(i int) StructField
			（3）多层成员访问时，根据[]int提供的每个结构体的字段索引，返回字段信息，没有找到时返回零值。当类型不是结构体或者索引超界时发生宕机
				FieldByIndex(index []int) StructField
			（4）通过名字查找获取 struct 字段
				FieldByName(name string) (StructField, bool)

			4.func 类型专用的方法
			（1）函数是否是不定参数函数
				IsVariadic() bool
			（2）输入参数个数
				NumIn() int
			（3）返回值个数
				NumOut() int
			（4）返回第 i 个输入参数类型
				In(i int) Type
			（5）返回第 i 个返回值类型
				Out(i int) Type

			5.map 类型专用的方法
				返回 map key 的 type
				Key() Type
	*/
	s := Student00{}
	rt := reflect.TypeOf(s)                 //返回一个 Type 的接口变量
	fieldName, ok := rt.FieldByName("Name") //通过名字查找获取 struct 字段

	//取 tag(标签) 数据
	/*
		在 Go 语言中，标签是一种用于给结构体、字段或方法等编写元数据的注释。
		标签的格式为 key:"value"，其中 key 表示标签名称，value 是标签的值。标签通常用于实现序列化、ORM 等功能。
	*/
	if ok {
		fmt.Println(fieldName.Tag)
	}
	fieldAge, ok2 := rt.FieldByName("Age")

	/*
		可以像 JSON 一样，取 tag 里的数据，多个 tag 之间无逗号，tag 不需要引号
	*/
	if ok2 {
		fmt.Println(fieldAge.Tag.Get("a"))
		fmt.Println(fieldAge.Tag.Get("b"))
	}

	fmt.Println("type_Name:", rt.Name())         //返回包含包名的类型名字，对于未命名类型返回的是空 （type_Name: Student00）
	fmt.Println("type_NumField:", rt.NumField()) //返回字段数目 （type_NumField: 2）
	fmt.Println("type_PkgPath:", rt.PkgPath())   //返回类型的包路径，如果类型是预声明类型或未命名类型，则返回空字符串（type_PkgPath: main）
	fmt.Println("type_String:", rt.String())     //返回类型的字符串表示形式，该字符串包含类型的包路径和名称 （type_String: main.Student00）

	fmt.Println("type.Kind:", rt.Kind()) //返回该类型的底层基础类型
	fmt.Println("type.Kind.String:", rt.Kind().String())
	fmt.Println("type.String()=", rt.String())

	//获取结构类型的字段名称
	for i := 0; i < rt.NumField(); i++ {
		fmt.Printf("type.Field[%d].Name := %v\n", i, rt.Field(i).Name) //通过整数索引获取 struct 字段
	}

	sc := make([]int, 20)
	sc = append(sc, 1, 2, 3)
	sct := reflect.TypeOf(sc)

	//返回类型的元素类型，该方法只适合 array, chan, ptr, slice 类型
	scet := sct.Elem()

	fmt.Println("slice element type.Kind() =", scet.Kind())
	fmt.Printf("slice element type.Kind() = %d", scet.Kind())
	fmt.Println("slice element type.String() =", scet.String())

	fmt.Println("slice element type.Name() =", scet.Name())
	fmt.Println("slice type.NumMethod() =", scet.NumMethod()) //返回一个类型的方法的个数
	fmt.Println("slice type.PkgPath() =", scet.PkgPath())     //空
	fmt.Println("slice type.PkgPath() =", sct.PkgPath())      //空
}
